from notify.signal import Signal
import abc, copy
from core import util

class MetaData(object):
    ''' '''
    def __init__(self, py_type):
        self.py_type = py_type
    


class DataHandle(object):
    ''' Represents the data that exists on each component instance, 
        which corrosponds to an attribute.
        It can be thought of as an instance of attribute instances.
    '''
    __slots__ = ('instance', 'attr', '_data', 'change')
    
    def __init__(self, instance, attr):
        self.instance = instance
        self.attr = attr
        self._data = attr.default
        self.change = Signal()
        
    def child_handles(self): return None
    
    def __getstate__(self):
        return {'_data':self._data, 'instance':self.instance, 'attr':self.attr}
    
    def __setstate__(self, state):
        self._data = state['_data']
        self.instance = state['instance']
        self.attr = state['attr']
    
    def name(self): return str(self.instance.name) + "." + str(self.attr.name)
        
    def __str__(self): return str(self._data)
        
    def get(self, proxy=None):
        inst = self.instance if proxy is None else proxy
        get_name = "get_"+str(self.attr.name)
        value = self._data
        if hasattr(inst, get_name):
            value = getattr(inst, get_name)()
        return self.attr.get(inst, value, self)
            
            
    def set(self, value, signal=True):
        value = self.attr.set(self.instance, value, self)
        self._data = value
        #self.instance.data_changed(self.attr, self._data)
        if signal:
            self.signal_change()
            self.instance.data_changed(self)
        
    
    def copy(self):
        return copy.copy(self._data)
        
    def signal_change(self):
        value = self._data
        self.change(value)
        if hasattr(self.instance, 'on_data_change'):
            self.instance.on_data_change(value)

class DataHandleList(DataHandle):

    def __init__(self, instance, attr):
        #list.__init__(self)
        DataHandle.__init__(self, instance, attr)
        #if not isinstance(self._data, list):
        self._data = []

    def child_handles(self): return self._data

    def _data_list(self):
        data = self.get()
        result = []
        for item in data._data:
            if isinstance(item, DataHandle):
                result.append(item.get())
            else:
                result.append(item)
        return result
        #return [item.get() for item in data._data]

    data = property(_data_list)

    '''
    def __getstate__(self):
        data_dict = self.__dict__
        del data_dict['change']
        return data_dict
    
    def __setstate__(self, state):
        self._data = state['_data']
    '''


    def clear(self):
        self._data = []

    def add_item(self):
        handler = self.attr.create_item_handler(self.instance)
        self.append(handler)
        self.signal_change()

    def __cast(self, other):
        if isinstance(other, DataHandleList): return other._data_list()
        else: return other

    #def __repr__(self): 
    #    return str(self._data_list())

    #def __str__(self): return self.__repr__()

    def __iter__(self): return iter(self._data)

    def __lt__(self, other):
        return self is not other and self._data_list() <  self.__cast(other)

    def __le__(self, other):
        return self is other or self.data <= self.__cast(other)

    def __eq__(self, other):
        return self is other or self.data == self.__cast(other)

    def __ne__(self, other):
        return self is not other and self.data != self.__cast(other)

    def __gt__(self, other):
        return self is not other and self.data >  self.__cast(other)

    def __ge__(self, other):
        return self is other or self.data >= self.__cast(other)

    def __contains__(self, item):
        return item in self.data

    def __len__(self):
        return len(self.data)

    def __iter__(self): return iter(self._data_list())

    def __getitem__(self, i):
        return self._data[i].get()

    def __setitem__(self, i, item):
        self.data[i] = item

    def __delitem__(self, i):
        del self.data[i]

    def __getslice__(self, i, j):
        return self.data[i:j]

    def __setslice__(self, i, j, other):
        self.data[i:j] = list(other)

    def __delslice__(self, i, j):
        del self.data[i:j]

    def __add__(self, other):
        return self.data + other

    def __radd__(self, other):
        return other + self.data

    def __iadd__(self, other):
        self._p_note_change()
        if isinstance(other, PersistentList):
            self.data += other.data
        else:
            self.data += list(other)
        return self

    def __mul__(self, n):
        return self.__class__(self.data * n)

    __rmul__ = __mul__

    def __imul__(self, n):
        self.data *= n
        return self

    def append(self, item):
        
        if isinstance(item, DataHandle):
            self._data.append(item)
            return
        if util.isiter(item):
            list_handle = self.attr.create_handle(self.instance)
            for i in item:
                list_handle.append(i)
            self._data.append(list_handle)
            return
            
        handler = self.attr.create_item_handler(self.instance)
        handler.set(item)
        self._data.append(handler)

    def insert(self, i, item):
        self._p_note_change()
        self.data.insert(i, item)

    def pop(self, i=-1):
        return self.data.pop(i)

    def remove(self, item):
        if isinstance(item, DataHandle):
            self._data.remove(item)
        else:
            self.data.remove(item)
        self.signal_change()

    def count(self, item):
        return self.data.count(item)

    def index(self, item, *args):
        return self.data.index(item, *args)

    def reverse(self):
        self.data.reverse()

    def sort(self, *args, **kwargs):
        self.data.sort(*args, **kwargs)
    
    '''
    def extend(self, other):
        if isinstance(other, PersistentList):
            self.data.extend(other.data)
        else:
            self.data.extend(other)
    '''
    
class TreeNode(object):
    def __init__(self, data, parent=None, children=None):
        self.data = data
        self.children = [] if not children else children
        self.parent = parent
        
    def add_child(self, data):
        if not isinstance(data, TreeNode):
            node = TreeNode(data)
        else:
            node = data
        self.children.append(node)
        node.parent = self
        return node
    
    def pprint(self, space=" "):
        for node in self.walk():
            parents = node.num_parents()-1
            print(space*parents + str(node.data))
            
    #def __repr__(self): return "<TreeNode " + str(self.data) + ">"
    
    def __str__(self): return "<TreeNode " + str(self.data) + ">"
       
    def parents(self):
        nodes = []
        parent_node = self.parent
        if not parent_node: return []
        while parent_node:
            nodes.append(parent_node)
            parent_node = parent_node.parent
        return nodes
            
    def num_parents(self): return len(list(self.parents()))
            
    def walk(self):
        if not self.children: return
        for child in self.children:
            yield child
            for sub_child in child.walk():
                yield sub_child
    
    def from_list(self, L):
        self._walk_list(self, L)
        
    def _walk_list(self, parent, L):
        if parent is None:
            return
        if not util.isiter(L):
            parent.add_child(L)
        else:
            last_parent = parent
            for item in L:
                if util.isiter(item):
                    self._walk_list(last_parent, item)    
                else:
                    child = parent.add_child(item)
                    last_parent = child
    
    
    
    
class TreeHandler(DataHandle):
    
    def __init__(self, instance, attr):
        DataHandle.__init__(self, instance, attr)
        self._data = TreeNode(None)
    
    def get_parent(self):
        return self._data.parent
    
    parent = property(get_parent)
    
    def clear(self):
        self._data = TreeNode(None)
    
    def child_handles(self): 
        handles = []
        for child in self._data.children:
            handles.append(self.handler(child))
        return handles
    
    def add_child(self, data):
        return self._data.add_child(data)
        
    def get_item(self, data):
        for node in self.walk():
            if node.data == data:
                return node
    
    def node(self):
        return self._data
    
    def handler(self, node, instance=None):
        handler = self.attr.create_item_handler(self.instance)
        handler.set(node.data, signal=False)
        return handler
    
    def walk(self):
        for node in self.node().walk():
            yield node
            #for sub_child in child.walk():
            #    yield sub_child
    
    
    
